﻿using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

public class MapGenerator : MonoBehaviour {
    #region 变量
    public const int mapChunkSize = 241; // 边上顶点的数量
    [Range (0, 6)]
    public int EditorPreviewLOD; // 细节等级
    public DrawMode drawMode; // 显示方式
    public Noise.NormalizeMode normalizeMode; // 单位化噪声的方式
    public float noiseScale; // 缩放
    public AnimationCurve meshHeightCurve; // 曲线
    public float meshMultiplier; //地图的上下幅度
    public bool autoUpdate; // 修改后自动生成
    public bool useFalloffMap; // 是否使用下降贴图
    public int octaves;
    [Range (0, 1)]
    public float persistance;
    public float lacunarity;
    public int seed; // 随机种子
    public Vector2 offset; // 偏移
    public TerrainType[] regions; // 区域（地图的类型）
    private Queue<MapThreadInfo<MapData>> mapThreadInfoQueue = new Queue<MapThreadInfo<MapData>> ();
    private Queue<MapThreadInfo<MeshData>> meshDataThreadInfoQueue = new Queue<MapThreadInfo<MeshData>> ();
    private float[, ] falloffMap;

    #endregion

    #region 生命周期函数
    private void Awake () {
        falloffMap = FalloffGenerator.GenerateFalloffMap (mapChunkSize);
    }
    private void Update () {
        // 执行线程队列中的方法
        if (mapThreadInfoQueue.Count > 0) {
            for (int i = 0; i < mapThreadInfoQueue.Count; i++) {
                MapThreadInfo<MapData> threadInfo = mapThreadInfoQueue.Dequeue ();
                threadInfo.callback (threadInfo.parameter);
            }
        }
        if (meshDataThreadInfoQueue.Count > 0) {
            for (int i = 0; i < meshDataThreadInfoQueue.Count; i++) {
                MapThreadInfo<MeshData> threadInfo = meshDataThreadInfoQueue.Dequeue ();
                threadInfo.callback (threadInfo.parameter);
            }
        }
    }
    #endregion
    #region 自定义函数

    /// <summary>
    /// 请求获取MapData
    /// </summary>
    /// <param name="callback">获取完成时调用的函数</param>
    public void RequestMapData (Vector2 center, Action<MapData> callback) {
        // 创建线程
        ThreadStart threadStart = delegate {
            MapDataThread (center, callback);
        };

        // 启动线程
        new Thread (threadStart).Start ();
    }

    private void MapDataThread (Vector2 center, Action<MapData> callback) {
        // 在线程中生成地图数据并将数据添加到map线程队列中
        MapData mapData = GeneraMapData (center);
        // 添加lock防止出现问题
        lock (mapThreadInfoQueue) {
            mapThreadInfoQueue.Enqueue (new MapThreadInfo<MapData> (callback, mapData));
        }
    }
    public void RequestMeshData (MapData mapData, int lod, Action<MeshData> callback) {
        // 创建线程
        ThreadStart threadStart = delegate {
            MeshDataThread (mapData, lod, callback);
        };
        // 启动线程
        new Thread (threadStart).Start ();
    }
    private void MeshDataThread (MapData mapData, int lod, Action<MeshData> callback) {
        // 在线程中生成Mesh数据然后添加到mesh线程队列中
        MeshData meshData = MeshGenerator.GenerateTerrainMesh (mapData.heightMap, meshHeightCurve, meshMultiplier, lod);
        // 添加lock防止出现问题
        lock (meshDataThreadInfoQueue) {
            meshDataThreadInfoQueue.Enqueue (new MapThreadInfo<MeshData> (callback, meshData));
        }
    }
    /// <summary>
    /// 在编辑器中时以这种方式生成地图
    /// </summary>
    public void DrawMapInEditor () {
        MapData mapData = GeneraMapData (Vector2.zero);

        // FindObjectOfType(type): 返回第一个类型为type的  已加载  的  激活  对象，没有匹配的对象时返回null
        MapDisplay display = FindObjectOfType<MapDisplay> ();
        // 判断是否存在
        if (display == null) {
            Debug.LogWarning ("场景中没有MapDisplay");
            return;
        }
        // 判断显示方式然后显示噪声图
        switch (drawMode) {
            case DrawMode.NoiseMap:
                display.DrawTexture (TextureGenerator.TextureFromHeightMap (mapData.heightMap));
                break;
            case DrawMode.ColorMap:
                display.DrawTexture (TextureGenerator.TextureFromColorMap (mapData.colourMap, mapChunkSize, mapChunkSize));
                break;
            case DrawMode.Mesh:
                display.DrawMesh (MeshGenerator.GenerateTerrainMesh (mapData.heightMap, meshHeightCurve, meshMultiplier, EditorPreviewLOD), TextureGenerator.TextureFromColorMap (mapData.colourMap, mapChunkSize, mapChunkSize));
                break;
            case DrawMode.FalloffMap:
                display.DrawTexture (TextureGenerator.TextureFromHeightMap (FalloffGenerator.GenerateFalloffMap (mapChunkSize)));
                break;
            default:
                break;
        }
    }

    /// <summary>
    /// 生成地图数据
    /// </summary>
    /// <returns>数据</returns>
    private MapData GeneraMapData (Vector2 center) {
        // 噪声图
        float[, ] noiseMap = Noise.GenerateNoiseMap (mapChunkSize, mapChunkSize, seed, noiseScale, octaves, persistance, lacunarity, center + offset, normalizeMode);

        // 颜色图
        Color[] colorMap = new Color[mapChunkSize * mapChunkSize];

        // 给对应高度的地形更改颜色
        for (int y = 0; y < mapChunkSize; y++) {
            for (int x = 0; x < mapChunkSize; x++) {

                if (useFalloffMap) {
                    noiseMap[x, y] = Mathf.Clamp01 (noiseMap[x, y] - falloffMap[x, y]);
                }

                float currentHeight = noiseMap[x, y];
                // 根据高度查找匹配的颜色
                for (int i = 0; i < regions.Length; i++) {
                    if (currentHeight >= regions[i].height) {
                        // 存下颜色
                        colorMap[y * mapChunkSize + x] = regions[i].colour;
                    } else {
                        break;
                    }
                }
            }
        }
        return new MapData (noiseMap, colorMap);
    }

    // 这个函数当脚本被修改就会被执行
    private void OnValidate () {
        // 通过这样的形式可以对修改值进行控制
        // if (mapChunkSize < 1) mapChunkSize = 1;更改为使用mapChunkSize之后不需要此判断
        // if (mapChunkSize < 1) mapChunkSize = 1;
        if (lacunarity < 1) lacunarity = 1;
        if (octaves < 0) octaves = 0;

        falloffMap = FalloffGenerator.GenerateFalloffMap (mapChunkSize);
    }

    #endregion
    struct MapThreadInfo<T> {
        public readonly Action<T> callback; // 回调函数
        public readonly T parameter; // 调用时候调用的参数

        public MapThreadInfo (Action<T> callback, T parameter) {
            this.callback = callback;
            this.parameter = parameter;
        }
    }

}
#region 定义
/// <summary>
/// 显示噪声图的方式
/// </summary>
public enum DrawMode {
    NoiseMap,
    ColorMap,
    Mesh,
    FalloffMap,
}

/// <summary>
/// 地形类型
/// </summary>
[System.Serializable]
public struct TerrainType {
    public string name; // 名称
    public float height; // 高度
    public Color colour; // 颜色
}

/// <summary>
/// 地图数据
/// </summary>
public struct MapData {
    public readonly float[, ] heightMap;
    public readonly Color[] colourMap;

    public MapData (float[, ] heightMap, Color[] colourMap) {
        this.heightMap = heightMap;
        this.colourMap = colourMap;
    }
}
#endregion